﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Reflection;
using System.ComponentModel;
using System.Security;
using System.Security.Permissions;

using HttpJson = PickGold.Web.Json;

namespace PickGold.Web.Controls
{
	/// <summary>
	/// 脚本标签。
	/// </summary>
	//[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
	//[ParseChildren(false)]
	//[PersistChildren(true)]
	//[Designer(typeof(Designer))]
	[ControlBuilder(typeof(Builder))]
	[ToolboxData("<{0}:Script runat=\"server\" />")]
	public class Script : Control, ITextControl
	{
		/// <summary>
		/// 获取或设置控件的文本内容。
		/// </summary>
		public string Text { get; set; }

		/// <summary>
		/// 脚本语言。
		/// </summary>
		public ScriptLanguage Language { get; set; }

		/// <summary>
		/// 脚本类型。
		/// </summary>
		public ScriptType Type { get; set; }

		/// <summary>
		/// 字符集。
		/// </summary>
		public string Charset { get; set; }

		/// <summary>
		/// 引用路径。
		/// </summary>
		[UrlProperty]
		public string Source { get; set; }

		/// <summary>
		/// 是否延迟加载。
		/// </summary>
		public ScriptDefer Defer { get; set; }

		/// <summary>
		/// 是否生成脚本标签
		/// </summary>
		[DefaultValue(false)]
		public bool NoTag { get; set; }

		private void RenderBeginTag(HtmlTextWriter writer)
		{
			if (this.NoTag && string.IsNullOrEmpty(this.Source))
				return;

			var language = this.Language;
			var type = this.Type;
			if (type == ScriptType.Unknow && language == ScriptLanguage.Unknow)
				type = ScriptType.JavaScript;
			if (language == ScriptLanguage.Unknow && type != ScriptType.Unknow)
			{
				switch (type)
				{
					case ScriptType.JScript:
						language = ScriptLanguage.JScript;
						break;
					case ScriptType.JavaScript:
						language = ScriptLanguage.JavaScript;
						break;
					case ScriptType.EcmaScript:
						language = ScriptLanguage.EcmaScript;
						break;
					case ScriptType.VbScript:
						language = ScriptLanguage.VbScript;
						break;
					default:
						language = ScriptLanguage.JavaScript;
						type = ScriptType.JavaScript;
						break;
				}
			}
			if (type == ScriptType.Unknow && language != ScriptLanguage.Unknow)
			{
				switch (language)
				{
					case ScriptLanguage.JScript:
						type = ScriptType.JScript;
						break;
					case ScriptLanguage.JavaScript:
						type = ScriptType.JavaScript;
						break;
					case ScriptLanguage.EcmaScript:
						type = ScriptType.EcmaScript;
						break;
					case ScriptLanguage.VbScript:
						type = ScriptType.VbScript;
						break;
					default:
						type = ScriptType.JavaScript;
						language = ScriptLanguage.JavaScript;
						break;
				}
			}
			switch (language)
			{
				case ScriptLanguage.JScript:
					writer.AddAttribute("language", "jscript");
					break;
				case ScriptLanguage.JavaScript:
					writer.AddAttribute("language", "javascript");
					break;
				case ScriptLanguage.EcmaScript:
					writer.AddAttribute("language", "ecmascript");
					break;
				case ScriptLanguage.VbScript:
					writer.AddAttribute("language", "vbscript");
					break;
				default:
					writer.AddAttribute("language", "javascript");
					break;
			}
			switch (type)
			{
				case ScriptType.JScript:
					writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/jscript");
					break;
				case ScriptType.JavaScript:
					writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
					break;
				case ScriptType.EcmaScript:
					writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/ecmascript");
					break;
				case ScriptType.VbScript:
					writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/vbscript");
					break;
				default:
					writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
					break;
			}
			if (!string.IsNullOrEmpty(this.Charset))
				writer.AddAttribute("charset", this.Charset);
			if (!string.IsNullOrEmpty(this.Source))
			{
				var source = this.Source;
				if (source[0] == '~')
				{
					source = source.Replace(':', '\0');
					source = this.ResolveClientUrl(source);
					source = source.Replace('\0', ':');
				}
				writer.AddAttribute(HtmlTextWriterAttribute.Src, source);
			}
			if (this.Defer == ScriptDefer.Defer)
				writer.AddAttribute("defer", "defer");
			writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
			writer.RenderBeginTag(HtmlTextWriterTag.Script);
		}

		/// <summary>
		/// 将此控件内容发送到提供的 System.Web.UI.HtmlTextWriter 对象，此对象编写将在客户端呈现的内容。
		/// </summary>
		/// <param name="writer">接收控件内容的 System.Web.UI.HtmlTextWriter 对象。</param>
		protected override void Render(HtmlTextWriter writer)
		{
			var defer = this.Defer;
			var source = this.Source;
			if (defer == ScriptDefer.Load && (string.IsNullOrEmpty(source) || source.IndexOf(':') > 1 || source.IndexOf('?') > 1))
				defer = ScriptDefer.Unknow;
			if (defer == ScriptDefer.Load)
			{
				if (source.IndexOf(':') < 0)
					source = this.MapPathSecure(source);
				if (!File.Exists(source))
				{
					source = this.Source;
					defer = ScriptDefer.Unknow;
				}
			}
			if (!string.IsNullOrEmpty(this.Source) && defer != ScriptDefer.Load)
			{
				this.RenderBeginTag(writer);
				writer.RenderEndTag();
			}

			//if (!this.HasControls() && string.IsNullOrEmpty(this.Text) && (string.IsNullOrEmpty(this.Source) || defer != ScriptDefer.Load))
			//if (string.IsNullOrEmpty(this.Source) || defer != ScriptDefer.Load)
			//	return;

			source = this.Source;
			this.Source = null;
			this.RenderBeginTag(writer);
			this.Source = source;
			writer.WriteLine("<!--<![CDATA[");
			if (defer == ScriptDefer.Load)
			{
				if (string.IsNullOrEmpty(this.Charset))
					writer.WriteLine(File.ReadAllText(source));
				else
					writer.WriteLine(File.ReadAllText(source, Encoding.GetEncoding(this.Charset)));
			}
			writer.WriteLine(this.Text);
			this.RenderChildren(writer);
			if (this.NoTag && string.IsNullOrEmpty(this.Source))
				return;

			writer.Write("//]]>-->");
			writer.RenderEndTag();
		}

		#region static from json

		/// <summary>
		/// 序列化成Json字符串
		/// </summary>
		/// <param name="graph">要转换的对象</param>
		/// <returns>用Json表示对象的字符串</returns>
		public static string ToString(object graph)
		{
			return HttpJson.ToString(graph);
		}
		/// <summary>
		/// 序列化成Json字符串
		/// </summary>
		/// <param name="graph">要转换的对象</param>
		/// <param name="staticMember">是否成生静态成员对象</param>
		/// <returns>用Json表示对象的字符串</returns>
		public static string ToString(object graph, bool staticMember)
		{
			return HttpJson.ToString(graph, staticMember);
		}
		/// <summary>
		/// 序列化成Json字符串
		/// </summary>
		/// <param name="graph">要转换的对象</param>
		/// <param name="bindingFlags">指示序列化成员的标志</param>
		/// <returns>用Json表示对象的字符串</returns>
		public static string ToString(object graph, BindingFlags bindingFlags)
		{
			return HttpJson.ToString(graph, bindingFlags);
		}

		/// <summary>
		/// 字符串的JS转义处理
		/// </summary>
		/// <param name="input">输入字符串</param>
		/// <returns>经过JS转义处理后的字符串</returns>
		public static string JsEncode(string input)
		{
			return StringUtil.JsEncode(input);
		}
		/// <summary>
		/// 字符串的JS转义处理
		/// </summary>
		/// <param name="input">输入字符串</param>
		/// <param name="invertedComma">是否使用单引号作为直接字符串常量分界符</param>
		/// <returns>经过JS转义处理后的字符串</returns>
		public static string JsEncode(string input, bool invertedComma)
		{
			return StringUtil.JsEncode(input, invertedComma);
		}
		/// <summary>
		/// 字符串的JS转义处理
		/// </summary>
		/// <param name="input">输入字符串</param>
		/// <param name="stringDivide">直接字符串常量分界符</param>
		/// <returns>经过JS转义处理后的字符串</returns>
		public static string JsEncode(string input, char stringDivide)
		{
			return StringUtil.JsEncode(input, stringDivide);
		}
		/// <summary>
		/// 字符串的JS转义处理
		/// </summary>
		/// <param name="input">输入字符串</param>
		/// <param name="stringDivide">直接字符串常量分界符</param>
		/// <returns>经过JS转义处理后的字符串</returns>
		public static string JsEncode(string input, string stringDivide)
		{
			return StringUtil.JsEncode(input, stringDivide);
		}

		#endregion
	}

	/// <summary>
	/// 脚本语言。
	/// </summary>
	public enum ScriptLanguage
	{
		/// <summary>
		/// 未知。
		/// </summary>
		Unknow = 0,
		/// <summary>
		/// vbscript
		/// </summary>
		VbScript = 1,
		/// <summary>
		/// ecmascript
		/// </summary>
		EcmaScript = 2,
		/// <summary>
		/// javascript
		/// </summary>
		JavaScript = 3,
		/// <summary>
		/// jscript
		/// </summary>
		JScript = 4,
	}

	/// <summary>
	/// 脚本类型。
	/// </summary>
	public enum ScriptType
	{
		/// <summary>
		/// 未知。
		/// </summary>
		Unknow = 0,
		/// <summary>
		/// type/vbscript
		/// </summary>
		VbScript = 1,
		/// <summary>
		/// type/ecmascript
		/// </summary>
		EcmaScript = 2,
		/// <summary>
		/// type/javascript
		/// </summary>
		JavaScript = 3,
		/// <summary>
		/// type/jscript
		/// </summary>
		JScript = 4,
	}

	/// <summary>
	/// 脚本是否延迟加载枚举。
	/// </summary>
	public enum ScriptDefer
	{
		/// <summary>
		/// 不确定。
		/// </summary>
		Unknow = 0,
		/// <summary>
		/// defer
		/// </summary>
		Defer = 1,
		/// <summary>
		/// 直接加载文件。
		/// </summary>
		Load = 2
	}
}
